package org.zuel.huhuForum.service.Impl;

import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import org.zuel.huhuForum.constant.CommunityConstant;
import org.zuel.huhuForum.dao.um.UserMapper;
import org.zuel.huhuForum.model.User;
import org.zuel.huhuForum.model.UserExample;
import org.zuel.huhuForum.param.LoginTicket;
import org.zuel.huhuForum.service.IUserService;
import org.zuel.huhuForum.util.CommunityUtil;
import org.zuel.huhuForum.util.PasswdUtil;
import org.zuel.huhuForum.util.RedisKeyUtil;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Log4j2
public class UserService implements IUserService, CommunityConstant {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private  UserMapper userMapper;



    /**
     * 优先从缓存中取值
     * @param userId
     * @return
     */
    private User getCache(int userId) {
        String redisKey = RedisKeyUtil.getUserKey(userId);
        return (User) redisTemplate.opsForValue().get(redisKey);
    }

    /**
     * 缓存中没有该用户信息时，则将其存入缓存
     * @param userId
     * @return
     */
    private User initCache(int userId) {
        User user = userMapper.selectByPrimaryKey(userId);

        user.setCreateTime(null);
        user.setUpdateTime(null);
        String redisKey = RedisKeyUtil.getUserKey(userId);
        redisTemplate.opsForValue().set(redisKey, user, 120, TimeUnit.SECONDS);
        return user;
    }

    /**
     * 用户信息变更时清除对应缓存数据
     * @param userId
     */
    private void clearCache(int userId) {
        String redisKey = RedisKeyUtil.getUserKey(userId);
        redisTemplate.delete(redisKey);
    }

    @Override
    public User findUserById(int id) {
        User user = getCache(id); // 优先从缓存中查询数据
        if (user == null) {
            user = initCache(id);
        }
        return user;
    }

    @Override
    public User findUserByName(String username) {
        return userMapper.selectByName(username);
    }

    @Override
    public Map<String, Object> register(User user) throws InvalidKeySpecException, NoSuchAlgorithmException {
        Map<String, Object> map = new HashMap<>();

        if (user == null) {
            throw new IllegalArgumentException("参数不能为空");
        }
        if (StringUtils.isBlank(user.getUsername())) {
            map.put("usernameMsg", "账号不能为空");
            return map;
        }

        if (StringUtils.isBlank(user.getPassword())) {
            map.put("passwordMsg", "密码不能为空");
            return map;
        }

        if (StringUtils.isBlank(user.getEmail())) {
            map.put("emailMsg", "邮箱不能为空");
            return map;
        }

        // 验证账号是否已存在
        User u = userMapper.selectByName(user.getUsername());
        if (u != null) {
            map.put("emailMsg", "该用户名已存在");
            return map;
        }

        // 验证邮箱是否已存在
        u = userMapper.selectByEmail(user.getEmail());
        if (u != null) {
            map.put("emailMsg", "该邮箱已被注册");
            return map;
        }

        String passwd= PasswdUtil.privateDecrypt(user.getPassword());
        // 注册用户
        user.setPassword(CommunityUtil.md5(passwd)); // 加密

        user.setType(0); // 默认普通用户
        user.setStatus(0); // 默认未激活
        user.setActivationCode(CommunityUtil.generateUUID()); // 激活码

        // 随机头像（用户登录后可以自行修改）
        user.setHeaderUrl(null);
        user.setCreateTime(new Date()); // 注册时间

        userMapper.insert(user);

//        // 给注册用户发送激活邮件
//        Context context = new Context();
//        context.setVariable("email", user.getEmail());
//        // http://localhost:8080/echo/activation/用户id/激活码
//        String url = domain + contextPath + "/activation/" + user.getId() + "/" + user.getActivationCode();
//        context.setVariable("url", url);
//        String content = templateEngine.process("/mail/activation", context);
//        mailClient.sendMail(user.getEmail(),"激活 Echo 账号", content);

        return map;
    }

    @Override
    public Map<String, Object> login(String email, String password, int expiredSeconds) throws InvalidKeySpecException, NoSuchAlgorithmException {

            Map<String, Object> map = new HashMap<>();

            // 空值处理
            if (StringUtils.isBlank(email)) {
                map.put("msg", "emailMsg");
                map.put("code", "23");
                map.put("data", "账号不能为空");
                return map;
            }
            if (StringUtils.isBlank(password)) {
                map.put("msg", "passwordMsg");
                map.put("code", "23");
                map.put("data", "密码不能为空");
                return map;
            }
            // 验证账号
            User user = userMapper.selectByEmail(email);
            if (user == null) {
                map.put("msg", "emailMsg");
                map.put("code", "23");
                map.put("data", "该账号不存在");
                return map;
            }
            // 验证状态
            if (user.getStatus() == 0) {
                // 账号未激活
                map.put("msg", "emailMsg");
                map.put("code", "23");
                map.put("data", "该账号未激活");
                return map;
            }
            String true_passwd=PasswdUtil.privateDecrypt(password);
            // 验证密码
            String md_password = CommunityUtil.md5(true_passwd);
            if (!user.getPassword().equals(md_password)) {
                map.put("msg", "passwordMsg");
                map.put("code", "23");
                map.put("data", "密码错误");

                return map;
            }
            // 用户名和密码均正确，为该用户生成登录凭证
            LoginTicket loginTicket = new LoginTicket();
            loginTicket.setUserId(user.getId());
            loginTicket.setTicket(CommunityUtil.generateUUID()); // 随机凭证
            loginTicket.setStatus(0); // 设置凭证状态为有效（当用户登出的时候，设置凭证状态为无效）
            loginTicket.setExpired(new Date(System.currentTimeMillis() + expiredSeconds * 1000)); // 设置凭证到期时间

            // 将登录凭证存入 redis
            String redisKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket());
            redisTemplate.opsForValue().set(redisKey, loginTicket,expiredSeconds,TimeUnit.SECONDS);
            map.put("userId", user.getId());
            map.put("ticket", loginTicket.getTicket());
            map.put("user", user);
            return map;
    }

    @Override
    public Map<String, Object> loginma(String email, int expiredSeconds) {
        Map<String, Object> map=new HashMap();

        // 验证账号
        User user = userMapper.selectByEmail(email);
        if (user == null) {
            map.put("msg", "emailMsg");
            map.put("code", "23");
            map.put("data", "该账号不存在");
            return map;
        }

        // 验证状态
        if (user.getStatus() == 0) {
            // 账号未激活
            map.put("msg", "emailMsg");
            map.put("code", "23");
            map.put("data", "该账号未激活");
            return map;
        }

        //为该用户生成登录凭证
        LoginTicket loginTicket = new LoginTicket();
        loginTicket.setUserId(user.getId());
        loginTicket.setTicket(CommunityUtil.generateUUID()); // 随机凭证
        loginTicket.setStatus(0); // 设置凭证状态为有效（当用户登出的时候，设置凭证状态为无效）
        loginTicket.setExpired(new Date(System.currentTimeMillis() + expiredSeconds * 1000)); // 设置凭证到期时间

        // 将登录凭证存入 redis
        String redisKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket());
        redisTemplate.opsForValue().set(redisKey, loginTicket,expiredSeconds,TimeUnit.SECONDS);
        map.put("userId", user.getId());
        map.put("ticket", loginTicket.getTicket());
        map.put("user", user);
        return map;
    }

    @Override
    public void logout(String ticket) {
       // 修改（先删除再插入）对应用户在 redis 中的凭证状态
        String redisKey = RedisKeyUtil.getTicketKey(ticket);
        LoginTicket loginTicket = (LoginTicket) redisTemplate.opsForValue().get(redisKey);
        loginTicket.setStatus(1);
        redisTemplate.opsForValue().set(redisKey, loginTicket);

    }

    @Override
    public LoginTicket findLoginTicket(String ticket) {
        String redisKey = RedisKeyUtil.getTicketKey(ticket);
        return (LoginTicket) redisTemplate.opsForValue().get(redisKey);
    }

    @Override
    public boolean updateState(Integer userid, Integer state) {
        User user=userMapper.selectByPrimaryKey(userid);
        if(user==null){
            return false;
        }
        user.setStatus(state);
        Integer flage=userMapper.updateByPrimaryKey(user);
        if(flage==0){
            return false;
        }
        return true;
    }

    @Override
    public List<User> selectState() {
        UserExample example=new UserExample();
        UserExample.Criteria criteria=example.createCriteria();
        criteria.andStatusEqualTo(0);
        List<User> list=userMapper.selectByExample(example);
        return list;
    }

    @Override
    public int updateHeader(int userId, String headUrl) {
        int rows = userMapper.updateHeader(userId, headUrl);
        clearCache(userId);
        return rows;
    }

    @Override
    public int updatePassword(int userId, String newPassword) throws InvalidKeySpecException, NoSuchAlgorithmException {
        User user = userMapper.selectById(userId);
        String passwd=PasswdUtil.privateDecrypt(newPassword);
        // 重新加盐加密
        newPassword = CommunityUtil.md5(passwd);
        clearCache(userId);
        return userMapper.updatePassword(userId, newPassword);
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities(int userId) {
        User user = this.findUserById(userId);
        List<GrantedAuthority> list = new ArrayList<>();
        list.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                switch (user.getType()) {
                    case 1:
                        return CommunityConstant.AUTHORITY_ADMIN;
                    case 2:
                        return CommunityConstant.AUTHORITY_MODERATOR;
                    default:
                        return CommunityConstant.AUTHORITY_USER;
                }
            }
        });
        return list;
    }

    @Override
    public Map<String, Object> doResetPwd(String email, String password) throws InvalidKeySpecException, NoSuchAlgorithmException {

            Map<String, Object> map = new HashMap<>(2);
            if (StringUtils.isBlank(password)) {
                map.put("msg", "errMsg");
                map.put("code", "23");
                map.put("data", "密码不能为空");
                return map;
            }
            User user = userMapper.selectByEmail(email);
            if (user == null) {
                map.put("msg", "errMsg");
                map.put("code", "23");
                map.put("data", "未发现账号");
                return map;
            }
            String newpasswd=PasswdUtil.privateDecrypt(password);
            final String passwordEncode = CommunityUtil.md5(newpasswd );
            int i = userMapper.updatePassword(user.getId(), passwordEncode);
            if (i <= 0) {
                map.put("msg", "errMsg");
                map.put("code", "23");
                map.put("data", "修改数据库密码错误");
            } else {
                clearCache(user.getId());
            }
            return map;
    }
}
